/*
 * Copyright (c) 2009 Sylvestre Gallon <syl@pmbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#import <stdlib.h>
#import <unistd.h>
#import "sdlengine.h"

/*
 * Helpers
 */

id
GetState(struct scene_head *head, uint32_t st)
{
	struct scene *sc;

	TAILQ_FOREACH(sc, head, entry) {
		if (sc->ident == st) {
			LOG(LOG_INFO, "Switch to scene : %s\n", sc->name);
			return sc->object;
		}
	}
	return nil;
}

@implementation sdlengine

- (id) init {
	self = [super init];

	if (self) {
		xml = [[xmloader alloc] init];
		if (![xml config_exist]) {
			[xml config_create];
		} else {
			[xml load];
		}
		
		TAILQ_INIT(&allscene);

		/* Some debug to verify the loading of config */
		LOG(LOG_DEBUG, "Dump config\n");
		LOG(LOG_DEBUG, "- loglvl: %i\n", [[config instance] loglvl]);
		LOG(LOG_DEBUG, "- logfile: %s\n", [[config instance] logfile]);
	}

	return self;
}

- (void) addscene: (struct scene *) sc {
	TAILQ_INSERT_TAIL(&allscene, sc, entry);
}

- (int) engineinit {
	if (TAILQ_EMPTY(&allscene)) {
		LOG(LOG_ERR, "You need to have at least one scene\n");
		return EXIT_FAILURE;
	}
	curr_state = TAILQ_FIRST(&allscene)->object;

	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
		LOG(LOG_ERR, "SDL_Init failed\n");
		return EXIT_FAILURE;
	}
		
	videoinf = SDL_GetVideoInfo();
	if (!videoinf) {
		LOG(LOG_ERR, "SDL_GetVideoInfo failed\n");
		return EXIT_FAILURE;
	}

	videofl = SDL_OPENGL;
	videofl |= SDL_GL_DOUBLEBUFFER;
	videofl |= SDL_HWPALETTE;
	videofl |= SDL_RESIZABLE;
	if (videoinf->hw_available)
		videofl |= SDL_HWSURFACE;
	else
		videofl |= SDL_SWSURFACE;
	if (videoinf->blit_hw)
		videofl |= SDL_HWACCEL;

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	surf = SDL_SetVideoMode(800, 600, 32, videofl);
	if (!surf) {
		LOG(LOG_ERR, "SDL_SetVideoMode failed\n");
		return EXIT_FAILURE;
	}

	[curr_state resizewidth: 800 andheight: 600];

	return (EXIT_SUCCESS);
}

- (int) initOpenGL {
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	return EXIT_SUCCESS;
}

- (int) dojob {
	int st;
	int isActive = TRUE;
	int toRet;

	while (42) {
		while(SDL_PollEvent(&event)) {
			if ((st = [curr_state changeState]) != STATE_UNCHANGED) {
				curr_state = GetState(&allscene, st);
			}

			switch (event.type) {
			case SDL_ACTIVEEVENT:
				if (event.active.gain == 0)
					isActive = FALSE;
				else
					isActive = TRUE;
				break;
			case SDL_VIDEORESIZE:
				surf = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, videofl);
				if (!surf) {
					toRet = EXIT_FAILURE;
					goto end_dojob;
				}
	
				[curr_state resizewidth: event.resize.w andheight: event.resize.h];
				break;
			case SDL_KEYDOWN:
				if (![curr_state keys: event]) {
					toRet = EXIT_SUCCESS;
					goto end_dojob;
				}
				break;
			case SDL_QUIT:
				toRet = EXIT_SUCCESS;
				goto end_dojob;
				break;
			default:
				break;
			}
			if (isActive)
				[curr_state drawscene];	
		}
	}
end_dojob:
	return EXIT_SUCCESS;
}

- (void) release {
	struct scene *sc;

	while ((sc = TAILQ_FIRST(&allscene))) {
		TAILQ_REMOVE(&allscene, sc, entry);
		[sc->object release];
		free(sc);
	}

	[xml release];
	SDL_Quit();
	[super release];
}

@end
